﻿using AntMovement.Core;
using AntMovement.Core.Attributes;
using AntMovement.Core.Entity;
using AntMovement.Core.Enum;
using AntMovement.Core.IParameter;
using AntMovement.Core.Logic;
using AntMovement.Core.Manager;
using AntMovement.Model;
using AntMovement.Parameter;
using AntMovement.Tasks;
using AntMovernent.SolidTechESerialCard;
using Prism.Ioc;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Loader;

namespace AntMovement.Domain
{
    public class TaskManager : ITaskManager
    {
        private CancellationTokenSource _tokenSource;
        private TemporaryParameter _temporary;

        private Dictionary<MotionType, IMotionBase> _motionBases;
        private List<IHardwareUpdate> _hardwares;
        private IOParameter _ioPara;
        private Parameter.AxisParameter _axisPara;
        private List<TaskModel> _tasks;

        private IDialogHost _dialogHost;
        private IContainerExtension _containerProvider;
        private ILog _log;
        private IHoldParameter _holdParameter;
        public TaskManager(IContainerExtension containerProvider, ITemporaryParameter temporaryParameter, IIOParameter iOParameter, IAxisParameter axisParameter, IDialogHost dialogHost, ILog log)
        {
            _log = log;
            _containerProvider = containerProvider;
            _holdParameter = _containerProvider.Resolve<IHoldParameter>();
            _tasks = new List<TaskModel>();
            _dialogHost = dialogHost;
            _ioPara = (IOParameter)iOParameter;
            _axisPara = (Parameter.AxisParameter)axisParameter;
            _temporary = (TemporaryParameter)temporaryParameter;
            _tokenSource = new CancellationTokenSource();
            _motionBases = new Dictionary<MotionType, IMotionBase>();
            _hardwares = new List<IHardwareUpdate>();
            ScanRegistrationTask(containerProvider);
            //Handle.BluingFilm.ModuleMotion moduleMotion = default;//不声明这个，程序集将不加载
            AntMovement.Handle.FPCAutomaticPlateSetting.Init init = default;
            //Handle.TPTest.Init init = default;
        }

        private Thread _thread;
        public async Task<bool> LoopTask()
        {
            _tasks.Clear();
            if (_tokenSource.Token.IsCancellationRequested)
                _tokenSource = new CancellationTokenSource();
            ScanMotion();
            InitHardwareUpdate();
            bool bl = await System.Threading.Tasks.Task.Run(() =>
            {
                foreach (var item in _motionBases)
                {
                    string error = item.Value.Open();
                    if (error != "ok")
                    {
                        _dialogHost.DialogShow($"打开控制卡通信失败，错误信息：{error}", LogLevel.Error, hostDialog: PopUpType.Global);
                        foreach (var item2 in _motionBases)
                        {
                            if (item2.Value.GetStatus() == 0)
                            {
                                item2.Value.Close();
                            }
                        }
                        return false;
                    }
                }
                return true;
            });
            if (bl)
            {
                foreach (var item in _hardwares)
                {
                    string error = item.Update();
                    if (error != "ok")
                    {
                        _log.Error("启动", "任务启动异常");
                        break;
                    }
                }
                _axisPara.InitAxisMotion(_log);
                _ioPara.InitAirCylinder(_log, _ioPara);
                _ioPara.InitSingleControl(_ioPara);
                GetTasks(_containerProvider);
                _thread = new Thread(new ThreadStart(() => 
                {
                    var io_EmergencyStop = _ioPara.DIDataSet.Where(x => x.AlarmEnable && x.Alarm.IsEMG);
                    _temporary.Status_Device = StatusDeviceType.手动;
                    _temporary.Status_Software = true;
                    Stopwatch stopwatch = new Stopwatch();
                    int count = -1;
                    NormalOpen reset = _ioPara.GetNormalOpen("复位");
                    NormalOpen stop = _ioPara.GetNormalOpen("停止");
                    Ton ton复位 = new Ton();
                    while (!_tokenSource.Token.IsCancellationRequested)
                    {
                        try
                        {
                            stopwatch.Start();
                            foreach (var item in _hardwares)
                            {
                                string error = item.Update();
                                if (error != "ok")
                                {
                                    Console.WriteLine(error);
                                    break;
                                }
                            }
                            #region 自动业务
                            foreach (var item in _tasks)
                            {
                                item.Handle.Handle();
                            }
                            #endregion
                            #region 安全停止
                            if (stop.Execute())
                            {
                                foreach (var item in _tasks)
                                {
                                    item.Handle.SafeStop();
                                }
                            }
                            #endregion
                            #region 急停
                            if (io_EmergencyStop.Any(x => !x.Status))
                            {
                                foreach (var item in _tasks)
                                {
                                    item.Handle.EmergencyStop();
                                }
                            }
                            #endregion
                            #region 复位
                            if (reset.Execute() && !_temporary.SystemStatus.Auto)
                            {
                                ton复位.Time(1000, true);
                                if (ton复位.Out)
                                {
                                    foreach (var item in _tasks)
                                    {
                                        item.Handle.Resetting();
                                    }
                                }
                            }
                            else
                            {
                                ton复位.Time(1000, false);
                            }
                            #endregion

                        }
                        catch (Exception ex)
                        {
                            _log.Error("异常", $"系统崩溃:[{ex}]");
                            _temporary.SystemStatus.Auto = false;
                            _temporary.SystemStatus.AlarmStop = true;
                        }
                        _holdParameter.Save();
                        stopwatch.Stop();
                        if (count >= 20)
                        {
                            count = -1;
                            _temporary.SystemStatus.SweepCycle = stopwatch.Elapsed.TotalMilliseconds;
                        }
                        count++;
                        if (stopwatch.Elapsed.TotalMilliseconds > 1 && stopwatch.Elapsed.TotalMilliseconds < 10)
                        {
                            if (stopwatch.Elapsed.TotalMilliseconds > 1)
                            {
                                Thread.Sleep((int)(10 - stopwatch.Elapsed.TotalMilliseconds));
                            }
                            else
                            {
                                Thread.Sleep(10);
                            }
                        }
                        else
                        {
                            Thread.Sleep(10);
                        }
                        stopwatch.Reset();
                    }
                    foreach (var item2 in _motionBases)
                    {
                        if (item2.Value.GetStatus() == 0)
                        {
                            item2.Value.Close();
                        }
                    }
                    _temporary.Status_Software = false;
                    _temporary.Status_Device = StatusDeviceType.板卡未连接;
                }));
                _thread.Priority = ThreadPriority.Highest;
                _thread.Start();
                return true;
            }
            _motionBases.Clear();
            return false;
        }

        public void StopTask() => _tokenSource.Cancel();

        private void ScanMotion()
        {
            MotionType[] motionTypes = _ioPara.DIDataSet.Select(x => x.Type).ToArray();
            foreach (var motionType in motionTypes.Distinct()) 
            {
                if (!_motionBases.ContainsKey(motionType))
                {
                     var motiontypec = _ioPara.DIDataSet.FirstOrDefault(x => x.Type == motionType);
                    if (motiontypec != null)
                    {
                        _motionBases.Add(motionType, GetMotionBase(motionType, motiontypec.MotionName, motiontypec.CardNo, motiontypec.IP));
                    }
                }
            }
            motionTypes = _ioPara.DODataSet.Select(x => x.Type).ToArray();
            foreach (var motionType in motionTypes.Distinct())
            {
                if (!_motionBases.ContainsKey(motionType))
                {
                    var motiontypec = _ioPara.DODataSet.FirstOrDefault(x => x.Type == motionType);
                    if (motiontypec != null)
                    {
                        _motionBases.Add(motionType, GetMotionBase(motionType, motiontypec.MotionName, motiontypec.CardNo, motiontypec.IP));
                    }
                }
            }
            motionTypes = _axisPara.AxisDataSet.Select(x => x.Type).ToArray();
            foreach (var motionType in motionTypes.Distinct())
            {
                if (!_motionBases.ContainsKey(motionType))
                {
                    var motiontypec = _axisPara.AxisDataSet.FirstOrDefault(x => x.Type == motionType);
                    if (motiontypec != null)
                    {
                        _motionBases.Add(motionType, GetMotionBase(motionType, motiontypec.MotionName, motiontypec.CardNo, motiontypec.IP));
                    }
                }
            }

        }

        private IMotionBase GetMotionBase(MotionType type, string name, int index, string ip)
        {
            switch (type)
            {
                case MotionType.PCI1230:return new AntMovement.PCI1230.Motion(name, index);
                case MotionType.SolidTech_E_SerialCard: return new AntMovernent.SolidTechESerialCard.Motion(ip, index, _axisPara.AxisDataSet);
                case MotionType.PCI9016: return new AntMovement.PCI9016.Motion(name, index);
                case MotionType.SMC308: return new AntMovement.SDK.LTSMC.Motion(ip, index, _axisPara.AxisDataSet.Where(x => x.Type == MotionType.SMC308).ToList());
                case MotionType.SMC304: return new AntMovement.SDK.LTSMC.Motion(ip, index, _axisPara.AxisDataSet.Where(x => x.Type == MotionType.SMC304).ToList());
            }
            return default;
        }

        private void InitHardwareUpdate()
        {
            foreach (var item in _motionBases)
            {
                var dis = _ioPara.DIDataSet.Where(x => x.Type == item.Key).ToList();
                if (dis.Count > 0)
                { 
                    dis.Sort((x, y) => x.CompareTo(y));
                    int length = dis.Count() / 16;
                    int count = 0;
                    while (length > count)
                    {
                        switch (item.Key)
                        {
                            case MotionType.PCI1230:
                                _hardwares.Add(new AntMovement.PCI1230.Domain.HardwareUpdateDI(15, dis.ToArray()));
                                count += 2;
                                break;
                            case MotionType.PCI9016:
                                _hardwares.Add(new AntMovement.PCI9016.Domain.HardwareUpdateDI(0, dis.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                            case MotionType.SolidTech_E_SerialCard:
                                _hardwares.Add(new AntMovernent.SolidTechESerialCard.Domain.HardwareUpdateDI(0, (ulong)count, dis.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                            case MotionType.SMC308:
                                _hardwares.Add(new AntMovement.SDK.LTSMC.Domain.Update.HardwareUpdateDI(0, (ulong)count, dis.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                            case MotionType.SMC304:
                                _hardwares.Add(new AntMovement.SDK.LTSMC.Domain.Update.HardwareUpdateDI(0, (ulong)count, dis.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                        }
                    }
                }
                var dos = _ioPara.DODataSet.Where(x => x.Type == item.Key).ToList();
                if (dos.Count > 0)
                {
                    dos.Sort((x, y) => x.CompareTo(y));
                    int length = dis.Count() / 16;
                    int count = 0;
                    while (length > count)
                    {
                        switch (item.Key)
                        {
                            case MotionType.PCI1230:
                                _hardwares.Add(new AntMovement.PCI1230.Domain.HardwareUpdateDO(15, dos.ToArray()));
                                count += 2;
                                break;
                            case MotionType.PCI9016:
                                _hardwares.Add(new AntMovement.PCI9016.Domain.HardwareUpdateDO(0, dos.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                            case MotionType.SolidTech_E_SerialCard:
                                _hardwares.Add(new AntMovernent.SolidTechESerialCard.Domain.HardwareUpdateDO(0, (ulong)count, dos.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                            case MotionType.SMC308:
                                _hardwares.Add(new AntMovement.SDK.LTSMC.Domain.Update.HardwareUpdateDO(0, (int)count, dos.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                            case MotionType.SMC304:
                                _hardwares.Add(new AntMovement.SDK.LTSMC.Domain.Update.HardwareUpdateDO(0, (int)count, dos.Skip((count * 16)).Take(16).ToArray()));
                                count += 1;
                                break;
                        }
                    }
                }
                var axiss = _axisPara.AxisDataSet.Where(x => x.Type == item.Key);
                foreach (var axis in axiss) 
                {
                    switch (item.Key)
                    {
                        case MotionType.SolidTech_E_SerialCard:
                            _hardwares.Add(new AntMovernent.SolidTechESerialCard.Domain.HardwareUpdateAxis(0, (uint)axis.AxisID, axis));
                            break;
                        case MotionType.PCI9016:
                            _hardwares.Add(new AntMovement.PCI9016.Domain.HardwareUpdateAxis(axis));
                            break;
                        case MotionType.SMC308:
                            _hardwares.Add(new AntMovement.SDK.LTSMC.Domain.Update.HardwareUpdateAxis(0, (ulong)axis.AxisID, axis));
                            break;
                        case MotionType.SMC304:
                            _hardwares.Add(new AntMovement.SDK.LTSMC.Domain.Update.HardwareUpdateAxis(0, (ulong)axis.AxisID, axis));
                            break;
                    }
                }
            }
        }

        private List<Type> GetFlowAttributesAll()
        {
            List<Type> result = new List<Type>();
            var dlls = AssemblyLoadContext.Default.Assemblies;
            foreach (var assemblie in dlls) 
            {
                var tasks = assemblie.GetTypes().Where(t => t.IsClass && t.GetCustomAttribute<FlowAttributes>() != null).ToList();
                foreach (var item in tasks)
                {
                    result.Add(item);
                }
            }
            return result;
        }

        private void ScanRegistrationTask(IContainerExtension containerProvider)
        {
            var ls = GetFlowAttributesAll();
            foreach (var item in ls)
            {
                containerProvider.Register(item, item.Name);
            }
        }

        private void GetTasks(IContainerExtension containerProvider)
        {
            try
            {
                var ls = GetFlowAttributesAll();
                foreach (var item in ls)
                {
                    ITask task = (ITask)containerProvider.Resolve(item, item.Name);
                    if (task != null)
                    {
                        var attr = item.GetCustomAttribute<FlowAttributes>();
                        _tasks.Add(new TaskModel()
                        {
                            Index = attr.Index,
                            Name = attr.Name,
                            Handle = task,
                        });
                    }
                }
                _tasks.Sort((x, y) => x.CompareTo(y));
            }
            catch (Exception)
            {
            }
        }
    }
}
